前端analysis | 知其所以然

jenkins pipeline 上传包失败

2025-08-28

1️⃣ 生成并配置 SSH 密钥

  1. 在 Jenkins 服务器上生成密钥(如果尚未有可用的):

    1
    2
    # -c 内容是注释,可读即可
    ssh-keygen -t ed25519 -C "jenkins@build"

    生成 ~/.ssh/id_ed25519id_ed25519.pub

  2. 将公钥添加到目标服务器

    1
    ssh-copy-id -i ~/.ssh/id_ed25519.pub user@target-server

    测试是否免密登录, 这部分很重要,可以-vvv查看具体过程

    1
    2
    3
    4
    5
    6
    7
    ssh user@target-server "echo OK"

    # user必须是目标服务器用户名, 在.ssh目录下执行
    ssh -vvv -i ~/.ssh/id_ed25519 \
    -o StrictHostKeyChecking=no \
    -o UserKnownHostsFile=/dev/null \
    user@host
  3. 在 Jenkins 中配置凭据

    • Jenkins → Manage Jenkins → Credentials → Global credentials

    • 新建 SSH Username with private key

    • 填:

      • ID:deploy-key (供 Pipeline 使用)
      • Username:目标服务器用户名
      • Private Key:直接粘贴 id_ed25519 内容或“从文件”读取

2️⃣ Jenkinsfile 示例

使用 Declarative Pipeline 方式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
pipeline {
agent any

environment {
REMOTE_HOST = 'your.server.com'
REMOTE_DIR = '/opt/apps'
APP_NAME = 'myapp'
TAR_FILE = 'myapp.tar.gz'
}

stages {
stage('Build') {
steps {
// 假设已经有构建脚本生成 tar.gz
sh 'tar -czf ${TAR_FILE} build_output/'
}
}

stage('Upload') {
steps {
sshagent(credentials: ['deploy-key']) {
sh """
scp -o StrictHostKeyChecking=no ${TAR_FILE} \
${REMOTE_HOST}:${REMOTE_DIR}/
"""
}
}
}

stage('Deploy') {
steps {
sshagent(credentials: ['deploy-key']) {
sh """
ssh -o StrictHostKeyChecking=no ${REMOTE_HOST} '
cd ${REMOTE_DIR} &&
tar -xzf ${TAR_FILE} -C ${REMOTE_DIR}/${APP_NAME} --strip-components=1 &&
systemctl restart myapp.service
'
"""
}
}
}
}
post {
success {
echo '部署完成 🎉'
}
failure {
echo '部署失败,请检查日志。'
}
}
}

关键点说明

  • sshagent:Pipeline 插件 SSH Agent 提供的步骤,会自动加载配置好的私钥。
  • StrictHostKeyChecking=no:避免首次连接需人工确认指纹。
  • --strip-components=1:解压时去掉顶层目录。
  • systemctl restart myapp.service:根据实际服务名修改。

3️⃣ 服务器端准备

  • 目标路径 /opt/apps/myapp 需存在并且 user 有写权限。
  • 如果用 systemd 管理应用,需要提前配置好 myapp.service

4️⃣ 常见问题排查

现象 解决
Jenkins 控制台提示 Permission denied (publickey) 检查私钥是否匹配、凭据 ID 是否正确、sshagent 插件是否安装
第一次连接要求输入 yes/no 已用 -o StrictHostKeyChecking=no
部署后旧文件残留 可在解压前执行 rm -rf ${APP_NAME}/*

5️⃣ 总结流程

  1. Jenkins 节点生成并注册 SSH Key
  2. 目标服务器加入公钥,实现免密
  3. Jenkinsfile:Build → Upload → Deploy
  4. 通过 sshagent 调用 scp & ssh 完成自动化部署

withcredentail vs sshagent

  1. 添加凭据

    • 类型SSH Username with private key
    • IDdeploy-key(示例 ID,后面 Pipeline 要用)
    • Username:目标服务器用户名
    • Private Key:粘贴私钥内容或选择文件

2️⃣ Jenkinsfile 示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
pipeline {
agent any

environment {
REMOTE_HOST = 'your.server.com'
REMOTE_DIR = '/opt/apps'
TAR_FILE = 'myapp.tar.gz'
APP_NAME = 'myapp'
}

stages {
stage('Build') {
steps {
sh 'tar -czf ${TAR_FILE} build_output/'
}
}

stage('Upload & Deploy') {
steps {
// 注入凭据
withCredentials([sshUserPrivateKey(credentialsId: 'deploy-key',
keyFileVariable: 'SSH_KEY',
usernameVariable: 'SSH_USER')]) {
sh """
# 上传文件
scp -i $SSH_KEY -o StrictHostKeyChecking=no ${TAR_FILE} \
$SSH_USER@${REMOTE_HOST}:${REMOTE_DIR}/

# 远程解压并重启
ssh -i $SSH_KEY -o StrictHostKeyChecking=no \
$SSH_USER@${REMOTE_HOST} '
mkdir -p ${REMOTE_DIR}/${APP_NAME} &&
tar -xzf ${REMOTE_DIR}/${TAR_FILE} -C ${REMOTE_DIR}/${APP_NAME} --strip-components=1 &&
systemctl restart myapp.service
'
"""
}
}
}
}
post {
success { echo '部署完成 🎉' }
failure { echo '部署失败,请检查日志。' }
}
}

关键点

  • withCredentials

    • credentialsId:与 Jenkins 中创建的凭据 ID 对应。
    • keyFileVariable:在步骤内会生成一个临时文件,路径保存在 $SSH_KEY 变量中。
    • usernameVariable:注入远程用户名。
  • -i $SSH_KEY:显式指定刚注入的私钥文件。

  • StrictHostKeyChecking=no:避免首次连接交互。


3️⃣ 调试:结合 -vvv

如果需要排查连接问题,可以直接在 sh 命令里加 -vvv

1
sh "ssh -vvv -i $SSH_KEY -o StrictHostKeyChecking=no $SSH_USER@${REMOTE_HOST} 'echo OK'"

这样在 Jenkins 控制台日志里能看到完整握手与认证细节。


对比 sshagent

  • sshagent 会在整个 block 内自动加载密钥到 ssh-agent,不用 -i

  • withCredentials 则给你一个真实的密钥文件,更适合你需要 scprsync 等显式指定密钥的场景。

  • 两者都安全,关键看团队习惯:

    • 频繁多处 sshsshagent 方便。
    • 只需一次 scp/sshwithCredentials 足够。

总结
withCredentials([sshUserPrivateKey(...)]) 让你在 Pipeline 里安全注入 SSH 私钥,并配合 scp/ssh -i $SSH_KEY 即可顺利上传 tar.gz 并远程部署,无需在 Jenkins 节点保存明文密钥文件。

QA 服务器忽略auth_keys

1
2
3
4
ls -ld /home/user /home/user/.ssh
ls -l /home/user/.ssh/authorized_keys
ls -lZ /home/user/.ssh/authorized_keys

1
2
3
4
getenforce
# 如果是 Enforcing
restorecon -Rv /home/user/.ssh

使用支付宝打赏
使用微信打赏

若你觉得我的文章对你有帮助,欢迎点击上方按钮对我打赏